package com.fast.alden.admin.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.fast.alden.admin.service.SysUserService;
import com.fast.alden.admin.util.SecurityUtils;
import com.fast.alden.common.entity.excel.UserExcelEntity;
import com.fast.alden.common.exception.BizException;
import com.fast.alden.common.exception.BizExceptionEnum;
import com.fast.alden.common.service.impl.BaseEntityServiceImpl;
import com.fast.alden.data.model.SysDepartment;
import com.fast.alden.data.model.SysUser;
import com.fast.alden.data.repo.SysDepartmentRepository;
import com.fast.alden.data.repo.SysUserPositionRepository;
import com.fast.alden.data.repo.SysUserRepository;
import com.fast.alden.data.repo.SysUserRoleRepository;
import jakarta.annotation.Nullable;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;

@Service
public class SysUserServiceImpl extends BaseEntityServiceImpl<SysUser, SysUserRepository> implements SysUserService {
    @Value("${fast-alden.secure.default-password}")
    private String defaultPassword;
    @Resource
    private SysUserRepository repository;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private SysDepartmentRepository departmentRepository;
    @Resource
    private SysUserRoleRepository userRoleRepository;
    @Resource
    private SysUserPositionRepository userPositionRepository;

    @Override
    protected Class<SysUser> getEClass() {
        return SysUser.class;
    }

    @Override
    protected SysUserRepository getRepository() {
        return repository;
    }

    @Override
    @Transactional
    public SysUser create(SysUser entity) {
        checkAccountBeforeCreate(entity);
        // 系统管理员只可以通过其他方式内置，不可手动创建
        // 所有手动创建的用户类型都是系统用户
        entity.setType(1);
        setUserDepartment(entity);
        entity.setPassword(passwordEncoder.encode(entity.getPassword()));
        return super.create(entity);
    }

    @Override
    @Transactional
    public SysUser update(SysUser entity) {
        if (entity == null) {
            throw new BizException(BizExceptionEnum.ENTITY_IS_NULL);
        }
        if (entity.getId() == null) {
            throw new BizException(BizExceptionEnum.ENTITY_ID_IS_NULL);
        }
        SysUser current = SecurityUtils.getCurrentUser();
        if (current != null && current.getId().equals(entity.getId())) {
            throw new BizException(BizExceptionEnum.CURRENT_USER_CAN_NOT_UPDATE_SELF);
        }
        SysUser user = this.retrieveWithException(entity.getId());
        checkAccountBeforeUpdate(entity, user);
        // 编辑用户不支持修改ID、密码、头像、用户类型
        user.setUsername(entity.getUsername());
        user.setNickname(entity.getNickname());
        user.setRealName(entity.getRealName());
        user.setEmail(entity.getEmail());
        user.setPhone(entity.getPhone());
        user.setBirthday(entity.getBirthday());
        user.setIntroduce(entity.getIntroduce());
        user.setStatus(entity.getStatus());
        setUserDepartment(entity);

        return super.update(user);
    }

    @Override
    @Transactional
    public List<SysUser> remove(List<Long> ids) {
        SysUser current = SecurityUtils.getCurrentUser();
        if (current != null && ids.contains(current.getId())) {
            throw new BizException(BizExceptionEnum.CURRENT_USER_CAN_NOT_DELETE_SELF);
        }
        List<SysUser> users = super.remove(ids);

        // 删除用户-角色关联数据
        userRoleRepository.deleteByUserIdIn(ids);
        // 删除用户-岗位关联数据
        userPositionRepository.deleteByUserIdIn(ids);

        return users;
    }

    @Override
    @Transactional
    public void updatePassword(Long userId, String newPassword) {
        SysUser current = SecurityUtils.getCurrentUser();
        if (current != null && current.getId().equals(userId)) {
            throw new BizException(BizExceptionEnum.CURRENT_USER_CAN_NOT_UPDATE_SELF);
        }
        SysUser user = this.retrieveWithException(userId);
        user.setPassword(passwordEncoder.encode(newPassword));
        repository.save(user);
    }

    @Override
    @Transactional
    public void resetPassword(Long userId, @Nullable String newPassword) {
        if (!StringUtils.hasText(newPassword)) {
            newPassword = defaultPassword;
        }
        updatePassword(userId, newPassword);
    }

    @Override
    @Transactional
    public void updateAvatar(Long userId, MultipartFile file) throws IOException, BizException {
        SysUser current = SecurityUtils.getCurrentUser();
        if (current != null && current.getId().equals(userId)) {
            throw new BizException(BizExceptionEnum.CURRENT_USER_CAN_NOT_UPDATE_SELF);
        }
        SysUser user = this.retrieveWithException(userId);

        InputStream inputStream = file.getInputStream();
        byte[] bytes = new byte[inputStream.available()];
        int size = inputStream.read(bytes);

        user.setAvatar("data:image/png;base64," + Base64.getEncoder().encodeToString(bytes));
        repository.save(user);
    }

    @Override
    @Transactional
    public void removeAvatar(Long userId) {
        SysUser current = SecurityUtils.getCurrentUser();
        if (current != null && current.getId().equals(userId)) {
            throw new BizException(BizExceptionEnum.CURRENT_USER_CAN_NOT_UPDATE_SELF);
        }
        SysUser user = this.retrieveWithException(userId);

        user.setAvatar(null);
        repository.save(user);
    }

    @Override
    @Transactional
    public void updateLoginInfo(SysUser entity) {
        if (entity == null) {
            throw new BizException(BizExceptionEnum.ENTITY_IS_NULL);
        }
        if (entity.getId() == null) {
            throw new BizException(BizExceptionEnum.ENTITY_ID_IS_NULL);
        }
        SysUser user = this.retrieveWithException(entity.getId());
        user.setLastLoginIp(entity.getLastLoginIp());
        user.setLastLoginTime(entity.getLastLoginTime());

        repository.save(user);
    }

    @Override
    public void exportExcel() throws IOException {
        // 查询数据
        List<SysUser> userList = repository.findAll();

        // 转化为Excel行数据
        List<UserExcelEntity> userRows = userList.stream().map(UserExcelEntity::loadFromModel).toList();

        // 写入excel
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();

        HttpServletResponse response = requestAttributes.getResponse();
        assert response != null;

        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");

        String fileName = URLEncoder.encode("UmsUser", StandardCharsets.UTF_8).replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        try (ExcelWriter writer = EasyExcel.write(response.getOutputStream(), UserExcelEntity.class).build()) {
            WriteSheet sheetUser = EasyExcel.writerSheet("User").build();
            writer.write(userRows, sheetUser);
        }
    }

    @Override
    @Transactional
    public void importExcel(MultipartFile file) throws IOException {
        EasyExcel.read(file.getInputStream(), UserExcelEntity.class, new PageReadListener<UserExcelEntity>(
                rows -> {
                    List<SysUser> list = rows.stream().map(UserExcelEntity::convertToModel).toList();
                    repository.saveAll(list);
                }
        )).sheet().doRead();
    }

    /**
     * 检查并更新用户所属部门
     */
    private void setUserDepartment(SysUser entity) {
        if (entity.getDepartmentId() == null) {
            throw new BizException(BizExceptionEnum.USER_DEPARTMENT_CAN_NOT_BE_NULL);
        }
        SysDepartment department = departmentRepository.findById(entity.getDepartmentId()).orElse(null);
        if (department == null) {
            throw new BizException(BizExceptionEnum.USER_DEPARTMENT_DOSE_NOT_EXIST);
        }
        entity.setDepartmentId(department.getId());
        entity.setDepartmentName(department.getName());
    }

    /**
     * 创建前校验账户信息
     */
    private void checkAccountBeforeCreate(SysUser target) {
        checkAccountBeforeCreateOrUpdate(target);
        if (repository.findByUsername(target.getUsername()).isPresent()) {
            throw new BizException(BizExceptionEnum.USERNAME_EXISTS);
        }
        if (repository.findByPhone(target.getPhone()).isPresent()) {
            throw new BizException(BizExceptionEnum.PHONE_EXISTS);
        }
        if (repository.findByEmail(target.getEmail()).isPresent()) {
            throw new BizException(BizExceptionEnum.EMAIL_EXISTS);
        }
    }

    /**
     * 修改前校验账户信息
     */
    private void checkAccountBeforeUpdate(SysUser target, SysUser source) {
        checkAccountBeforeCreateOrUpdate(target);
        if (!target.getUsername().equals(source.getUsername())) {
            if (repository.findByUsername(target.getUsername()).isPresent()) {
                throw new BizException(BizExceptionEnum.USERNAME_EXISTS);
            }
        }
        if (!target.getPhone().equals(source.getPhone())) {
            if (repository.findByPhone(target.getPhone()).isPresent()) {
                throw new BizException(BizExceptionEnum.PHONE_EXISTS);
            }
        }
        if (!target.getEmail().equals(source.getEmail())) {
            if (repository.findByEmail(target.getEmail()).isPresent()) {
                throw new BizException(BizExceptionEnum.EMAIL_EXISTS);
            }
        }
    }

    /**
     * 创建或修改前校验账户信息
     */
    private void checkAccountBeforeCreateOrUpdate(SysUser target) {
        if (!StringUtils.hasText(target.getUsername())) {
            throw new BizException(BizExceptionEnum.USERNAME_CANT_BE_EMPTY);
        }
        if (!StringUtils.hasText(target.getPhone())) {
            throw new BizException(BizExceptionEnum.PHONE_CANT_BE_EMPTY);
        }
        if (!StringUtils.hasText(target.getEmail())) {
            throw new BizException(BizExceptionEnum.EMAIL_CANT_BE_EMPTY);
        }
    }

    @Override
    public SysUser findByAccount(String account) {
        SysUser user = repository.findByUsername(account).orElse(null);
        if (user != null) {
            return user;
        }
        user = repository.findByPhone(account).orElse(null);
        if (user != null) {
            return user;
        }
        user = repository.findByEmail(account).orElse(null);
        return user;
    }
}
